package com.wang.tool.webtool;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.wang.tool.util.CommonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.convert.converter.Converter;

import java.io.IOException;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * BodyDateFormatConfig body反序列+序列
 * 支持 Date and LocalDateTime
 */
@Import({WebDateConverterConfig.BodyDateFormatConfig.class})
public class WebDateConverterConfig {

    /**
     * 日期时间格式 带毫秒
     */
    public static final String DATE_TIME_SSS_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";

    /**
     * 日期时间格式
     */
    public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";


    /**
     * 反序列化配置
     */
    private static List<DateTimeFormatter> deserializeFormatters = null;

    /**
     * 序列化配置
     */
    private static DateTimeFormatter serializeFormatter = null;


    /**
     * Date转换器，用于转换RequestParam和PathVariable参数
     */
    @Bean
    public Converter<String, Date> dateConverter() {
        return new Converter<String, Date>() {
            @Override
            public Date convert(String source) {
                for (DateTimeFormatter formatter : deserializeFormatters) {
                    try {
                        LocalDateTime localDateTime = LocalDateTime.parse(source, formatter);
                        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                    } catch (Throwable ignore) {
                    }
                }
                try {
                    long time = Long.parseLong(source);
                    return new Date(time);
                } catch (Throwable ignore) {
                }
                throw new IllegalArgumentException("can not convert source:" + source + " to Date");
            }

        };
    }


    /**
     * LocalDateTime转换器，用于转换RequestParam和PathVariable参数
     */
    @Bean
    public Converter<String, LocalDateTime> localDateTimeConverter() {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                for (DateTimeFormatter formatter : deserializeFormatters) {
                    try {
                        return LocalDateTime.parse(source, formatter);
                    } catch (Throwable ignore) {
                    }
                }
                try {
                    long time = Long.parseLong(source);
                    return LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault());
                } catch (Throwable ignore) {
                }
                throw new IllegalArgumentException("can not convert source:" + source + " to LocalDateTime");
            }
        };
    }


    @Autowired
    public void setDateFormatConfig(DateFormatConfig dateFormatConfig) {
        if (dateFormatConfig == null) {
            throw new IllegalArgumentException("dateFormatConfig can bot be null");
        }
        //反序列化
        List<String> deserializeFormats = dateFormatConfig.getDateDeserializeFormats();
        if (CommonUtil.isEmpty(deserializeFormats)) {
            deserializeFormatters = new ArrayList<>();
        } else {
            deserializeFormatters = deserializeFormats
                    .stream()
                    .map(f -> new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern(f))
                            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                            .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                            .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                            .toFormatter())
                    .collect(Collectors.toList());
        }
        //序列化非空
        String dateSerializeFormat = dateFormatConfig.getDateSerializeFormat();
        if (CommonUtil.isNotBlank(dateSerializeFormat)) {
            serializeFormatter = DateTimeFormatter.ofPattern(dateSerializeFormat);
        }
    }

    /**
     * requestbody和responsebody
     * 带有@JsonComponent注解的类或者静态内部类会自动注入jackson的序列化
     */
    @JsonComponent
    public static class BodyDateFormatConfig {


        public static class DateJsonSerializer extends JsonSerializer<Date> {
            @Override
            public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                if (serializeFormatter == null) {
                    jsonGenerator.writeNumber(date.getTime());
                } else {
                    LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                    jsonGenerator.writeString(serializeFormatter.format(localDateTime));
                }
            }
        }

        public static class DateJsonDeserializer extends JsonDeserializer<Date> {
            @Override
            public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                String source = jsonParser.getText();
                for (DateTimeFormatter formatter : deserializeFormatters) {
                    try {
                        LocalDateTime localDateTime = LocalDateTime.parse(source, formatter);
                        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                    } catch (Throwable ignore) {
                    }
                }
                try {
                    long time = Long.parseLong(source);
                    return new Date(time);
                } catch (Throwable ignore) {
                }
                throw new IllegalArgumentException("can not convert source:" + source + " to Date");
            }
        }


        public static class LocalDateTimeJsonSerializer extends JsonSerializer<LocalDateTime> {

            @Override
            public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                if (serializeFormatter == null) {
                    jsonGenerator.writeNumber(Timestamp.valueOf(localDateTime).getTime());
                } else {
                    jsonGenerator.writeString(serializeFormatter.format(localDateTime));
                }
            }
        }

        public static class LocalDateTimeJsonDeserializer extends JsonDeserializer<LocalDateTime> {
            @Override
            public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                String source = jsonParser.getText();
                for (DateTimeFormatter formatter : deserializeFormatters) {
                    try {
                        return LocalDateTime.parse(source, formatter);
                    } catch (Throwable ignore) {
                    }
                }
                try {
                    long time = Long.parseLong(source);
                    return LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault());
                } catch (Throwable ignore) {
                }
                throw new IllegalArgumentException("can not convert source:" + source + " to LocalDateTime");
            }

        }

    }

    /**
     * @Author wangshaopeng
     * @Date 2021/1/7
     */
    public static class DateFormatConfig {

        private List<String> dateDeserializeFormats;

        private String dateSerializeFormat;

        public List<String> getDateDeserializeFormats() {
            return dateDeserializeFormats;
        }

        public void setDateDeserializeFormats(List<String> dateDeserializeFormats) {
            this.dateDeserializeFormats = dateDeserializeFormats;
        }

        public String getDateSerializeFormat() {
            return dateSerializeFormat;
        }

        public void setDateSerializeFormat(String dateSerializeFormat) {
            this.dateSerializeFormat = dateSerializeFormat;
        }
    }
}